package ge.gen.eBdgt.action.graph;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URLEncoder;

import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.CombinedDomainCategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.chart.urls.CategoryURLGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.ui.TextAnchor;

public class CreateChart {

	public class MyGenerator implements CategoryURLGenerator,
			CategoryToolTipGenerator {
		private String prefix = "index.html";
		private String seriesParameterName = "series";
		private String categoryParameterName = "category";
		private String rangeParameterName = "range";
		private String rangeKey = null;
		private CreateChart createChart = null;

		private CategoryDataset theDataset = null;

		public MyGenerator(String prefix, CategoryDataset ds) {
			super();
			this.prefix = prefix;
			this.theDataset = ds;
		}

		public MyGenerator(String prefix) {
			this.prefix = prefix;
		}

		public MyGenerator(String prefix, String rangeKey,
				CreateChart createChart) {
			this.prefix = prefix;
			this.rangeKey = rangeKey;
			this.createChart = createChart;
		}

		public MyGenerator(String prefix, String seriesParameterName,
				String categoryParameterName) {
			this.prefix = prefix;
			this.seriesParameterName = seriesParameterName;
			this.categoryParameterName = categoryParameterName;
		}

		public MyGenerator(String prefix, String seriesParameterName,
				String categoryParameterName, String rangeParameterName,
				String rangeKey) {
			this.prefix = prefix;
			this.seriesParameterName = seriesParameterName;
			this.categoryParameterName = categoryParameterName;
			this.rangeParameterName = rangeParameterName;
			this.rangeKey = rangeKey;
		}

		public String myGenerateURL(Comparable seriesKey,
				Comparable categoryKey, Comparable rangeKey) {
			if (categoryKey.toString().equals("<<REST>>")
					|| seriesKey.toString().equals("<<REST>>")
					|| (rangeKey != null && rangeKey.toString().equals(
							"<<REST>>"))) {
				return "";
			}

			String url = this.prefix;
			boolean firstParameter = url.indexOf("?") == -1;

			if (categoryKey.toString().equals("rest_value")) {
				return "";
			}
			if (seriesKey.toString().equals("rest_serie")) {
				return "";
			}

			url += firstParameter ? "?" : "&";
			try {
				url += this.seriesParameterName + "="
						+ URLEncoder.encode(seriesKey.toString(), "UTF-8");
				url += "&" + this.categoryParameterName + "="
						+ URLEncoder.encode(categoryKey.toString(), "UTF-8");
				if (rangeKey != null) {
					url += "&" + this.rangeParameterName + "="
							+ URLEncoder.encode(rangeKey.toString(), "UTF-8");
				}
			} catch (java.io.UnsupportedEncodingException uee) {
				uee.printStackTrace();
			}

			return url;
		}

		public String myGenerateToolTip(Comparable seriesKey,
				Comparable categoryKey, Comparable rangeKey, Number value) {
			String text = "";
			if (this.rangeKey != null && !this.rangeKey.equals("<<NULL>>")) {
				text += this.rangeKey + ", ";
			}

			if (seriesKey != null && !seriesKey.toString().equals("<<NULL>>")) {
				text += seriesKey.toString() + ", ";
			}

			text += this.createChart.xAxis + "=" + categoryKey.toString()
					+ ", ";

			text += "value: " + value;
			return text;
		}

		/** Category **/

		public String generateURL(CategoryDataset dataset, int series,
				int category) {
			Comparable seriesKey = dataset.getRowKey(series);
			Comparable categoryKey = dataset.getColumnKey(category);

			return myGenerateURL(seriesKey, categoryKey, this.rangeKey);
		}

		public String generateToolTip(CategoryDataset dataset, int series,
				int category) {
			Comparable seriesKey = dataset.getRowKey(series);
			Comparable categoryKey = dataset.getColumnKey(category);

			return myGenerateToolTip(seriesKey, categoryKey, this.rangeKey,
					dataset.getValue(seriesKey, categoryKey));
		}

	}

	public static final boolean LEGEND = true;
	public static final boolean TOOL_TIPS = true;
	public static final boolean URLS = true;

	public String filebase;
	public JFreeChart chart;
	public CategoryDataset[] category_datasets;
	public String[] rangeNames;
	public String htmlFile;
	public String imageMapFile;
	public String imageFile;
	public String URLPrefix;

	public String chartTitle;
	public String xAxis;
	public String yAxis;
	public String type;
	public boolean stacked = false;
	public PlotOrientation orientation;

	public int rangeCount = 0;
	public int serieCount = 0;

	public int width = 600;
	public int height = 400;

	public CreateChart(String filebase, String URLPrefix) {
		super();
		this.filebase = filebase;
		this.htmlFile = filebase + ".html";
		this.imageMapFile = filebase + ".map";
		this.imageFile = filebase + ".png";
		this.URLPrefix = URLPrefix;
	}

	public void saveFiles() {

		// save it to an image
		try {
			ChartRenderingInfo info = new ChartRenderingInfo(
					new StandardEntityCollection());
			File file1 = new File(this.imageFile);
			ChartUtilities.saveChartAsPNG(file1, chart, this.width,
					this.height, info);

			// write an HTML page incorporating the image with an image map
			File file2 = new File(this.htmlFile);
			OutputStream out = new BufferedOutputStream(new FileOutputStream(
					file2));
			PrintWriter writer = new PrintWriter(out);
			writer.println("<HEAD><TITLE>JFreeChart Image Map</TITLE></HEAD>");
			writer.println("<BODY>");
			writer.println(ChartUtilities.getImageMap("chart", info));
			writer.println("<IMG SRC=\"" + this.imageFile + "\" " + "WIDTH=\""
					+ this.width + "\" HEIGHT=\"" + this.height
					+ "\" BORDER=\"0\" USEMAP=\"#chart\">");
			writer.println("</BODY>");
			writer.println("</HTML>");
			writer.close();

			File file3 = new File(this.imageMapFile);
			out = new BufferedOutputStream(new FileOutputStream(file3));
			writer = new PrintWriter(out);
			writer.println(ChartUtilities.getImageMap("chart", info));
			writer.close();

		} catch (IOException e) {
			System.out.println(e.toString());
		}

	}

	private Object callFunction(Object obj, String name) throws Exception {
		return callFunction(obj, name, new Object[] {});
	}

	private Object callFunction(Object obj, String name, Object arg)
			throws Exception {
		return callFunction(obj, name, new Object[] { arg });
	}

	private Object callFunction(Object obj, String name, Object[] args)
			throws Exception {
		Class[] classes = new Class[args.length];
		String classes_string = "";
		for (int i = 0; i < args.length; i++) {
			classes[i] = args[i].getClass();
			try {
				classes[i] = (Class) classes[i].getField("TYPE")
						.get(classes[i]);
			} catch (NoSuchFieldException ex) {
				// ok this is not a primitive type
			}
			classes_string += args[i].getClass().getName() + " ";
		}

		try {
			return callFunction(obj, name, args, classes);
		} catch (NoSuchMethodException e) {
			throw new Exception("Method " + obj.getClass().getName() + "."
					+ name + "(" + classes_string + ") not found.");
		}
	}

	private Object callFunction(Object obj, String name, Object[] args,
			Class[] classes) throws Exception {
		return obj.getClass().getMethod(name, classes).invoke(obj, args);
	}

	private void configureColors(Object renderer) throws Exception {
		Class[] pc = new Class[] { Integer.TYPE,
				Class.forName("java.awt.Paint") };
		java.awt.Color secondColor = Color.white;
		int i = 0;
		if (false) {
			for (java.awt.Paint item : ChartColor.createDefaultPaintArray()) {
				callFunction(renderer, "setSeriesPaint",
						new java.lang.Object[] {
								i,
								(java.awt.Paint) new GradientPaint(0.0f, 0.0f,
										(java.awt.Color) item, 1000, 0.0f,
										secondColor) }, pc);
				i += 1;
			}
		} else {
			for (java.awt.Paint item : ChartColor.createDefaultPaintArray()) {
				callFunction(renderer, "setSeriesPaint",
						new java.lang.Object[] { i, (java.awt.Paint) item }, pc);
				i += 1;
			}
		}

		for (java.awt.Paint item : ChartColor.createDefaultPaintArray()) {
			BufferedImage bi = new BufferedImage(2, 2,
					BufferedImage.TYPE_INT_RGB);
			Graphics2D big = bi.createGraphics();
			big.setColor((java.awt.Color) item);
			big.fillRect(0, 0, 1, 1);
			big.fillOval(1, 1, 2, 2);
			big.setColor(secondColor);
			big.fillRect(1, 0, 2, 1);
			big.fillOval(0, 1, 1, 2);
			Rectangle r = new Rectangle(0, 0, 2, 2);
			callFunction(renderer, "setSeriesPaint", new java.lang.Object[] {
					i, (java.awt.Paint) new TexturePaint(bi, r) }, pc);
			i += 1;
		}

		for (java.awt.Paint item : ChartColor.createDefaultPaintArray()) {
			BufferedImage bi = new BufferedImage(2, 2,
					BufferedImage.TYPE_INT_RGB);
			Graphics2D big = bi.createGraphics();
			big.setColor((java.awt.Color) item);
			big.fillRect(0, 0, 1, 1);
			big.fillOval(1, 0, 2, 1);
			big.setColor(secondColor);
			big.fillRect(0, 1, 1, 2);
			big.fillOval(1, 1, 2, 2);
			Rectangle r = new Rectangle(0, 0, 2, 2);
			callFunction(renderer, "setSeriesPaint", new java.lang.Object[] {
					i, (java.awt.Paint) new TexturePaint(bi, r) }, pc);
			i += 1;
		}
	}

	private void correctLegend(Object plot, Object subplot) throws Exception {

		Object li = callFunction(subplot, "getLegendItems");
		callFunction(plot, "setFixedLegendItems", new Object[] { li });
		if (((Integer) callFunction(li, "getItemCount")) == 1
				&& ((String) callFunction(callFunction(li, "get", 0),
						"getLabel")).equals("<<NULL>>")) {
			callFunction(plot, "setFixedLegendItems",
					new LegendItemCollection());
		}
	}

	private BarRenderer createBarRenderer() throws Exception {
		BarRenderer renderer;
		renderer = new StackedBarRenderer();
		ItemLabelPosition position1 = new ItemLabelPosition(
				ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER);
		ItemLabelPosition position2 = new ItemLabelPosition(
				ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_CENTER);
		renderer.setPositiveItemLabelPosition(position1);
		renderer.setNegativeItemLabelPosition(position2);
		renderer.setDrawBarOutline(false);
		configureColors(renderer);
		return renderer;
	}

	public void createBarChart() throws Exception {
		CategoryAxis categoryAxis = new CategoryAxis(this.xAxis);
		CombinedDomainCategoryPlot plot = new CombinedDomainCategoryPlot(
				categoryAxis);
		for (int range = 0; range < rangeCount; range++) {
			BarRenderer my_renderer = createBarRenderer();
			MyGenerator generator = new MyGenerator(this.URLPrefix,
					this.rangeNames[range], this);
			my_renderer.setBaseItemURLGenerator(generator);
			my_renderer.setBaseToolTipGenerator(generator);

			ValueAxis valueAxis = new NumberAxis(this.rangeNames[range]);
			valueAxis.setAxisLineVisible(false);
			if (this.rangeNames[range] == null) {
				valueAxis = new NumberAxis(this.yAxis);
			}

			CategoryPlot subplot = new CategoryPlot(
					this.category_datasets[range], null, valueAxis, my_renderer);

			plot.add(subplot, 1);

			// Correct Legend
			correctLegend(plot, subplot);

			subplot.setOrientation(this.orientation);

		}

		// NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
		this.chart = new JFreeChart(this.chartTitle,
				JFreeChart.DEFAULT_TITLE_FONT, plot, true);

		chart.setBackgroundPaint(Color.white);
		plot.setBackgroundPaint(Color.lightGray);
		plot.setDomainGridlinePaint(Color.white);
		plot.setRangeGridlinePaint(Color.white);

		// plot.clearDomainAxes();
		final CategoryAxis domainAxis = plot.getDomainAxis();
		domainAxis.setCategoryLabelPositions(CategoryLabelPositions
				.createUpRotationLabelPositions(Math.PI / 6.0));
		// OPTIONAL CUSTOMISATION COMPLETED.
	}

	public void createCategory1TestData() throws Exception {
		double[][] data = new double[][] { { 3.0, 4.0, 3.0, 5.0 },
				{ 5.0, 7.0, 6.0, 8.0 }, { 5.0, 7.0, 3.0, 8.0 },
				{ 1.0, 2.0, 3.0, 4.0 }, { 2.0, 3.0, 2.0, 3.0 } };
		CategoryDataset cd = DatasetUtilities.createCategoryDataset("Region ",
				"Sales/Q", data);
		this.category_datasets = new CategoryDataset[1];
		this.category_datasets[0] = cd;
	}

	public void loadDataFromStdIn() throws IOException {

		System.out.println("Chart title?");
		this.chartTitle = "test";

		System.out.println("Width?");
		this.width = 1000;
		System.out.println("Height?");
		this.height = 1000;

		System.out.println("X-Axis?");
		this.xAxis = "XAxis";

		System.out.println("Y-Axis?");
		this.yAxis = "YAxis";
		System.out.println("Series count?");
		serieCount = 10;

		System.out.println("Range count?");
		rangeCount = 10;

		this.category_datasets = new CategoryDataset[10];
		this.rangeNames = new String[rangeCount];

		for (int range = 0; range < rangeCount; range++) {
			rangeNames[range] = "test" + range;
			CategoryDataset category_dataset = new DefaultCategoryDataset();
			category_datasets[range] = category_dataset;
			for (int serie = 0; serie < serieCount; serie++) {
				System.out.println("Serie name?");
				String serieName = "series";
				int rowCount = 3;
				int columnCount = 6;
				for (int row = 0; row < rowCount; row++) {
					System.out.println("Category name?");
					String categoryName = "categ" + row;
					for (int col = 0; col < columnCount; col++) {
						String val = 10 * col + "";

						((DefaultCategoryDataset) category_dataset).addValue(
								Integer.parseInt(val), serieName, categoryName);

					}
				}
			}
		}
	}

	public static void main(String[] args) {
		try {
			CreateChart chart = new CreateChart(
					"icn_graph",
					"http://t00717420.psamer.ps.ge.com:8080/GenReqWorkLoad/detail.action;jsessionid=22E606214B1E61DBADD1ACA88088A007?icn=140918");
			chart.type = "bar";
			chart.stacked = true;
			chart.orientation = PlotOrientation.VERTICAL;
			chart.loadDataFromStdIn();
			// chart.createCategory1TestData();
			chart.createBarChart();
			chart.saveFiles();
			System.exit(0);
		} catch (Exception ex) {
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw, true);
			ex.printStackTrace(pw);
			pw.flush();
			sw.flush();
			System.out.print(sw.toString());
			System.out.println(ex.toString());
			System.exit(1);
		}

	}
}
